#include "Player.h"
#include <Windows.h>
#include "..\framework\InputManager.h"
#include "GameSettings.h"
#include <gl/glu.h>

// Player constants
const float_t RENDER_2D_FACTOR = 1.0f;
const float_t INIT_SPEED        = 12.0f;
const float_t MOVEMENT_BUFFER   = 10.0f;
const float_t CAMERA_HEIGHT     = 5.0f;
const float_t CAMERA_PITCH      = 30.0f;
const float_t PLAYER_JUMP_FORCE = 2500;
const float_t DEATH_TRIGGER_Y   = -100.0f;
const float_t MAX_DASH_TIME     = 0.2f;
const float_t DASH_SPEED        = 100.0f;
const float_t TORQUE_FACTOR     = 20.0f;
const float_t ILLEGAL_HEIGHT    = -100000;

float_t WINDOW_X      = -32;
float_t WINDOW_Y      = -24;
float_t WINDOW_WIDTH  = 64;
float_t WINDOW_HEIGHT = 48;

// Ctor
Player::Player()
	:
	 m_fTotalRotation(0.0f),
	 m_bDoubleJump(false),
	 m_bJumpEnabled(false),
	 m_bDashEnabled(false),
	 m_fDashTime(0.0f),
	 m_bStartPressed(false),
	 m_b2dEnabled(false),
	 m_fCacheX(0.0f),
	 m_fDeltaX(0.0f)
{
	// Init physics object
	m_physicsObject = new ImpossiblePhysics::PhysicsObj();
	m_physicsObject->idString = ImpossiblePhysics::PLAYER_OBJECT;
	m_fSpeed = INIT_SPEED;
	m_fDeltaX = 1.5f;
}


Player::~Player()
{
	delete m_physicsObject->body->getMotionState();
	delete m_physicsObject->body->getCollisionShape();

	delete m_physicsObject->body;
	delete m_physicsObject;
}

void Player::SetLookAt()
{
	// Reset model-view matrix
	glLoadIdentity();

	btTransform t;
	m_physicsObject->body->getMotionState()->getWorldTransform(t);
	btVector3 o = t.getOrigin();

	M3DMatrix44f m;
	m3dTranslationMatrix44(m, o.x(), o.y(), o.z());
	m3dInvertMatrix44(m, m);

	// Position the camera behind and above the player, then tilt it down
	glTranslatef(0.0f, -2, -10);
	glRotatef(CAMERA_PITCH, 1.0f, 0, 0);

	glMultMatrixf(m);
}

ImpossiblePhysics::PhysicsObj*& Player::GetPhysicsEntity()
{
	return m_physicsObject;
}

void Player::MoveForward(float_t argDeltaTime)
{
	// If player is on ground and moving forward, reset transform
	if(m_physicsObject->hit)
	{
		ResetTransform();
	}
	// Move Player forward
	m_physicsObject->body->translate(btVector3(0, 0, -m_fSpeed*argDeltaTime));

	// Move the camera if we're in 2D
	//if(m_b2dEnabled)
	//{
	//	glMatrixMode(GL_PROJECTION);
	//	glLoadIdentity();
	//	WINDOW_X += m_fDeltaX;
	//	glOrtho(WINDOW_X, WINDOW_X + WINDOW_WIDTH, WINDOW_Y+ WINDOW_HEIGHT, WINDOW_Y, 0, 1);
	//	glMatrixMode(GL_MODELVIEW);
	//}
}

void Player::MoveBackward(float_t argDeltaTime)
{
	// Move Player back
	m_physicsObject->body->translate(btVector3(0, 0, m_fSpeed*argDeltaTime));

	// Move the camera if we're in 2D
	//if(m_b2dEnabled)
	//{
	//	glMatrixMode(GL_PROJECTION);
	//	glLoadIdentity();
	//	WINDOW_X += m_fDeltaX;
	//	glOrtho(WINDOW_X, WINDOW_X + WINDOW_WIDTH, WINDOW_Y+ WINDOW_HEIGHT, WINDOW_Y, 0, 1);
	//	glMatrixMode(GL_MODELVIEW);
	//}
}

void Player::MoveRight(float_t argDeltaTime)
{
	if(!m_b2dEnabled)
	{
		m_physicsObject->body->translate(btVector3(m_fSpeed*argDeltaTime, 0, 0));
	}
	else
	{
		MoveForward(argDeltaTime);
	}
}

void Player::MoveLeft(float_t argDeltaTime)
{
	if(!m_b2dEnabled)
	{
		m_physicsObject->body->translate(btVector3(-m_fSpeed*argDeltaTime, 0, 0));
	}
	else
	{
		MoveBackward(argDeltaTime);
	}
}

void Player::ProcessInput(float_t argDeltaTime)
{
	if(InputManagerC::GetInstance()->IsMovingForward())
	{
		MoveForward(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingBackward())
	{
		MoveBackward(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingRight())
	{
		MoveRight(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingLeft())
	{
		MoveLeft(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsJumping())
	{
		Jump(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsDashing())
	{
		Dash(argDeltaTime);
	}
}

void Player::Update(float_t argDeltaTime)
{
	ProcessInput(argDeltaTime);
	CacheOrigin();
	UpdateDash(argDeltaTime);

	// Enable double jump if the player has landed
	if(m_physicsObject->hit)
	{
		m_bDoubleJump = true;
		//m_physicsObject->body->setAngularFactor(1.5f);
		//ResetTransform();
	}

	if(m_physicsObject->bottom < DEATH_TRIGGER_Y)
	{
		ResetPlayer();
		//m_bStartPressed = false;
		//m_physicsObject->body->setActivationState(ISLAND_SLEEPING);
	}

	if(m_b2dEnabled)
	{
		//Move the camera if we're in 2D
		if(m_b2dEnabled)
		{
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			WINDOW_X += m_fDeltaX;
			glOrtho(WINDOW_X, WINDOW_X + WINDOW_WIDTH, WINDOW_Y+ WINDOW_HEIGHT, WINDOW_Y, 0, 1);
			glMatrixMode(GL_MODELVIEW);
		}
	}
}

void Player::ResetPlayer()
{
	btTransform _transform;
	_transform.setOrigin(btVector3(0.0f, 5.0f, -2.0f));

	m_physicsObject->body->setCenterOfMassTransform(_transform);


	ResetTransform();
	m_physicsObject->body->setLinearVelocity(btVector3(0, 0, 0));

	// Reset camera with player if in 2D
	if(m_b2dEnabled)
	{
		WINDOW_X = -32;

		btTransform t;
		m_physicsObject->body->getMotionState()->getWorldTransform(t);
		btVector3 o = t.getOrigin();

		btVector3 _halfWidths = ((btBoxShape*)m_physicsObject->body->getCollisionShape())->getHalfExtentsWithMargin();

		float_t halfWidth = _halfWidths.x() * RENDER_2D_FACTOR;
		float_t x = (o.z() * -RENDER_2D_FACTOR) - halfWidth;

		float_t z = 1.5f - x;
		WINDOW_X = -32 + (x - 1.5f);
		m_fCacheX = x;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(WINDOW_X, WINDOW_X + WINDOW_WIDTH, WINDOW_Y+ WINDOW_HEIGHT, WINDOW_Y, 0, 1);
		glMatrixMode(GL_MODELVIEW);  
	}
}

void Player::CacheOrigin()
{
	btTransform t;
	m_physicsObject->body->getMotionState()->getWorldTransform(t);
	btVector3 o = t.getOrigin();

	// Calculate player's bottom border
	btVector3 _halfWidths = ((btBoxShape*)m_physicsObject->body->getCollisionShape())->getHalfExtentsWithMargin();
	m_physicsObject->bottom = o.y() - _halfWidths.y();

	// Save translated x position if we're in 2d so we can scroll camera
	if(m_b2dEnabled)
	{
		float_t _halfWidthX = _halfWidths.x() * RENDER_2D_FACTOR;
		float_t _currentX = (o.z() * -RENDER_2D_FACTOR) - _halfWidthX;

		m_fDeltaX = _currentX - m_fCacheX;
		m_fCacheX = _currentX;
	}
}


void Player::Jump(float_t argDelta)
{
	// Apply jump physics
	//ResetTransform();

	if(!m_bStartPressed)
	{
		m_physicsObject->body->setActivationState(DISABLE_DEACTIVATION);
		m_bStartPressed = true;
	}
	else
	{
		if(m_physicsObject->hit)
		{
			// Reset velocity so double jumps feel correct
			m_physicsObject->body->setLinearVelocity(btVector3(0, 0, 0));

			//m_physicsObject->body->setAngularFactor(1.5f);
			m_physicsObject->body->applyCentralForce(btVector3(0.0f, PLAYER_JUMP_FORCE, 0));

			// Don't apply torque in 2D
			if(!m_b2dEnabled)
			{
				m_physicsObject->body->applyTorque(btVector3(TORQUE_FACTOR, 0.0f, 0.0f));
			}
		
		}
		else if(m_bDoubleJump)
		{
			// Reset velocity so double jumps feel correct
			m_physicsObject->body->setLinearVelocity(btVector3(0, 0, 0));

			//m_physicsObject->body->setAngularFactor(1.5f);
			m_physicsObject->body->applyCentralForce(btVector3(0.0f, PLAYER_JUMP_FORCE, 0));

			// Don't apply torque in 2D
			if(!m_b2dEnabled)
			{
				m_physicsObject->body->applyTorque(btVector3(TORQUE_FACTOR, 0.0f, 0.0f));
			}
		
			m_bDoubleJump = false;
		}
	}
}

void Player::Dash(float_t argDelta)
{
	if(!m_bDashEnabled)
	{
		m_bDashEnabled = true;
	}
}

void Player::UpdateDash(float_t argDelta)
{
	if(m_bDashEnabled)
	{
		if(m_fDashTime < MAX_DASH_TIME)
		{
			m_physicsObject->body->translate(btVector3(0, 0, -DASH_SPEED * argDelta));
			m_fDashTime += argDelta;

			// Move the camera if we're in 2D
			//if(m_b2dEnabled)
			//{
			//	glMatrixMode(GL_PROJECTION);
			//	glLoadIdentity();
			//	WINDOW_X += m_fDeltaX;
			//	glOrtho(WINDOW_X, WINDOW_X + WINDOW_WIDTH, WINDOW_Y+ WINDOW_HEIGHT, WINDOW_Y, 0, 1);
			//	glMatrixMode(GL_MODELVIEW);
			//}
		}
		else
		{
			m_bDashEnabled = false;
			m_fDashTime = 0.0f;
		}
	}
}

void Player::SetMode3D(bool8_t arg3D)
{
	m_b2dEnabled = !arg3D;

	// Disable angular rotations in 2D
	if(m_b2dEnabled)
	{
		//m_physicsObject->body->setAngularFactor(0.0f);
		ResetTransform();
		m_physicsObject->body->setLinearVelocity(btVector3(0, 0, 0));
	}
	else
	{
		glMatrixMode (GL_PROJECTION);										// Select The Projection Matrix
		glLoadIdentity();													// Reset The Projection Matrix
		gluPerspective(45.0f,(GLfloat)Settings::WINDOW_WIDTH_3D/(GLfloat)Settings::WINDOW_HEIGHT_3D,0.1f,100.0f);

		glMatrixMode (GL_MODELVIEW);
		// Turn on angular velocity
		m_physicsObject->body->setAngularFactor(Settings::PLAYER_ANGULAR_FACTOR);
	}
}

//void Player::PlayJumpAnimation(int32_t argDelta)
//{
//	if(!m_bJumpAnimation) return;
//
//	// Check for full rotation
//	if(m_fTotalRotation >= M3D_PI || m_physicsObject->hit) 
//	{	
//		// Calculate 
//
//
//		m_fTotalRotation = 0.0f;
//		m_bJumpAnimation = false;
//		return;
//	}
//
//	// Update rotation value
//	m_fTotalRotation += ((float_t)argDelta / 1000.0f) * 2;
//
//	// Clamp rotation value
//	if(m_fTotalRotation >= M3D_PI)
//	{
//		m_fTotalRotation = M3D_PI;
//	}
//
//	btTransform _transform;
//	_transform = m_physicsObject->body->getWorldTransform();
//
//	btQuaternion _quaternion;
//	_quaternion.setEuler(0.0f, m_fTotalRotation, 0.0f);
//	_transform.setRotation(_quaternion);
//
//	m_physicsObject->body->setCenterOfMassTransform(_transform);
//}

void Player::ResetTransform()
{
	btTransform _transform;
	_transform = m_physicsObject->body->getWorldTransform();

	btQuaternion _quaternion;
	_quaternion.setEuler(0.0f, 0.0f, 0.0f);
	_transform.setRotation(_quaternion);

	m_physicsObject->body->setCenterOfMassTransform(_transform);
}

void Player::Draw() const
{
	glPushMatrix();
	// Transform modelview matrix by the rigid body's position
	btTransform _transform;
	float_t _mat[16];

	m_physicsObject->body->getMotionState()->getWorldTransform(_transform);
	_transform.getOpenGLMatrix(_mat);
	glMultMatrixf(_mat);

	// Draw cube
	glBegin(GL_QUADS);    

	// Grab dimensions
	btVector3 _halfWidths = ((btBoxShape*)m_physicsObject->body->getCollisionShape())->getHalfExtentsWithMargin();

	//// Top face
	//glColor3f(0.3f,0.3f,0.3f);
	//glVertex3f( _halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	//glVertex3f(-_halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	//glVertex3f(-_halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	//glVertex3f( _halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	//										 				  
	//// Bottom face							 				  
	//glColor3f(1.0f,0.5f,0.0f);
	//glVertex3f( _halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	//glVertex3f(-_halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	//glVertex3f(-_halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	//glVertex3f( _halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	//										 				  
	//// Front face							 				  
	//glColor3f(_halfWidths.x(),0.0f,0.0f);        			  
	//glVertex3f( _halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	//glVertex3f(-_halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	//glVertex3f(-_halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	//glVertex3f( _halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	//										 				  
	//// Back face							 				  
	//glColor3f(1.0f,0.0f,0.0f);         
	//glVertex3f( _halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	//glVertex3f(-_halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	//glVertex3f(-_halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	//glVertex3f( _halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	//										 				  
	//// Left face							 				  
	//glColor3f(0.0f,0.0f,1.0f);        			  
	//glVertex3f(-_halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	//glVertex3f(-_halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	//glVertex3f(-_halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	//glVertex3f(-_halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	//										 				  
	//// Right face							 				  
	//glColor3f(1.0f,0.0f,1.0f);          
	//glVertex3f( _halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	//glVertex3f( _halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	//glVertex3f( _halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	//glVertex3f( _halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());

	// Top face
	//glColor3f(0.3f,0.3f,0.3f);
	glNormal3f(0.0f, 1.0f, 0.0f);
	glVertex3f( _halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	glVertex3f(-_halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	glVertex3f(-_halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	glVertex3f( _halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
											 				  
	// Bottom face							 				  
	//glColor3f(1.0f,0.5f,0.0f);
	glNormal3f(0.0f, -1.0f, 0.0f);
	glVertex3f( _halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	glVertex3f(-_halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	glVertex3f(-_halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	glVertex3f( _halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
											 				  
	// Front face							 				  
	//glColor3f(_halfWidths.x(),0.0f,0.0f);        			  
	glNormal3f(0.0f, 0.0f, 1.0f);
	glVertex3f( _halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	glVertex3f(-_halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	glVertex3f(-_halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	glVertex3f( _halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
											 				  
	// Back face							 				  
	//glColor3f(1.0f,0.0f,0.0f);         
	glNormal3f(0.0f, 0.0f, -1.0f);
	glVertex3f( _halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	glVertex3f(-_halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	glVertex3f(-_halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	glVertex3f( _halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
											 				  
	// Left face							 				  
	//glColor3f(0.0f,0.0f,1.0f);        			  
	glNormal3f(-1.0f, 0.0f, 0.0f);
	glVertex3f(-_halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	glVertex3f(-_halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	glVertex3f(-_halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());      
	glVertex3f(-_halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
											 				  
	// Right face							 				  
	//glColor3f(1.0f,0.0f,1.0f);          
	glNormal3f(1.0f, 0.0f, 0.0f);
	glVertex3f( _halfWidths.x(), _halfWidths.y(),-_halfWidths.z());      
	glVertex3f( _halfWidths.x(), _halfWidths.y(), _halfWidths.z());      
	glVertex3f( _halfWidths.x(),-_halfWidths.y(), _halfWidths.z());      
	glVertex3f( _halfWidths.x(),-_halfWidths.y(),-_halfWidths.z());

	
	glEnd();

	glPopMatrix();
}

void Player::Draw2D()
{
	btTransform t;
	m_physicsObject->body->getMotionState()->getWorldTransform(t);
	btVector3 o = t.getOrigin();

	btVector3 _halfWidths = ((btBoxShape*)m_physicsObject->body->getCollisionShape())->getHalfExtentsWithMargin();

	float_t halfWidth = _halfWidths.x() * RENDER_2D_FACTOR;
	float_t x = (o.z() * -RENDER_2D_FACTOR) - halfWidth;
	float_t y = (o.y() * -RENDER_2D_FACTOR) - halfWidth;

	// draw

	glLoadIdentity();

	glColor3f(1.0f,0.0f,0.0f);  

	glBegin(GL_QUADS);

	glVertex3f(x + halfWidth * 2, y, 0);
	glVertex3f(x, y, 0);
	glVertex3f(x, y + halfWidth * 2, 0);
	glVertex3f(x + halfWidth * 2, y + halfWidth * 2, 0);


	glEnd();
}

